package com.bensonbasement.snorflux.server.verbs;

import com.bensonbasement.snorflux.commands.IAPFormula;
import com.bensonbasement.snorflux.commands.TargetMap;
import com.bensonbasement.snorflux.commands.TargetRequest;
import com.bensonbasement.snorflux.commands.TargetRequest.TargetType;
import com.bensonbasement.snorflux.commands.VerbInfo;
import com.bensonbasement.snorflux.server.world.Actor;
import com.bensonbasement.snorflux.server.world.Entity;


public abstract class Verb {
	
	private final String verbName;
	private final String iconGlyph;
	private final TargetRequest[] targetRequests;

	public Verb(String verbName, String iconGlyph, TargetRequest[] targetRequests) {
		super();
		this.verbName = verbName;
		this.iconGlyph = iconGlyph;
		this.targetRequests = targetRequests;
	}
	
	public Verb(String verbName, String iconGlyph) {
		this(verbName,iconGlyph,TargetRequest.NOTARGETS);
	}
	
	protected abstract void actuallyExecute(Actor actor, Entity object, TargetMap targets) throws VerbExecuteCanceledException;
	public abstract boolean canActorPerform(Actor actor);
	public abstract boolean checkRelevant(Actor actor, Entity object);
	public abstract boolean checkEnabled(Actor actor, Entity object);
	public abstract IAPFormula getAPFormula(Actor actor, Entity object);
	
	public final boolean validateTargets(TargetMap targets) {
		try {
			for (TargetRequest request : this.targetRequests) {
				if (request.getType() == TargetType.DIRECTION) {
					targets.getDirectionTarget(request.getTargetName());
				}
			}
		}
		catch (ClassCastException ex) {
			return false;
		}
		return true;
	}

	public final void execute(Actor actor, Entity object, TargetMap targets) throws VerbExecuteCanceledException {
		// TODO: Add check for adequate AP, and subtract the AP.
		if (this.validateTargets(targets) &&
				this.canActorPerform(actor) &&
				this.checkRelevant(actor,object) &&
				this.checkEnabled(actor,object)) {
			int apCost = getAPFormula(actor, object).calculateAPCost(targets);
			int originalAP = actor.getAP();
			if (apCost <= originalAP) {
				try {
					actor.spendAP(apCost);
					this.actuallyExecute(actor, object, targets);
				}
				catch (VerbExecuteCanceledException ex) {
					actor.setAP(originalAP);
					throw ex;
				}
			}
		}
	}
	
	public final VerbInfo render(Actor actor, Entity object) {
		if (! this.canActorPerform(actor) || ! this.checkRelevant(actor,object)) {
			return null;
		}
		else {
			Long objectID;
			if (object == null) {
				objectID = null;
			}
			else {
				objectID = object.getWorldElementID();
			}
			boolean enabled = this.checkEnabled(actor, object);
			IAPFormula apFormula = getAPFormula(actor, object);
			VerbInfo retVal = new VerbInfo(objectID, verbName, iconGlyph, targetRequests, apFormula, enabled);
			return retVal;
		}
	}


	public String getVerbName() {
		return verbName;
	}


	public String getIconGlyph() {
		return iconGlyph;
	}


	public TargetRequest[] getTargetRequests() {
		return targetRequests;
	}
	
	
}
